const { validate } = require('schema-utils');
const globby = require('globby');
const path = require('path');

const schema = {
  type: 'object',
  properties: {
    form: {
      type: 'string',
    },
    to: {
      type: 'string',
    },
    ignore: {
      type: 'array',
    },
  },
  additionalProperties: false, // 是否允许不存在的选项传入
};

class CopyWebpackPlugin {
  constructor(options = {}) {
    // 校验插件options
    validate(options, schema, {
      name: 'CopyWebpackPlugin',
    });
    this.options = options;
  }

  apply(compiler) {
    // 获取操作文件的对象
    const fs = compiler.outputFileSystem;

    // 绑定到 “thisCompilation” 钩子，
    // 以便进一步绑定到 compilation 过程更早期的阶段
    compiler.hooks.thisCompilation.tap('CopyWebpackPlugin', (compilation) => {
      // 绑定到 “additionalAssets” 钩子，为 compilation 创建额外 asset
      compilation.hooks.additionalAssets.tapAsync('CopyWebpackPlugin', async (callback) => {
        // 1. 获取ignore后的文件 paths
        const { form, ignore } = this.options;
        // to 默认打包目的根目录
        const to = this.options.to ? this.options.to : '.';

        // 运行指令的上下文
        const { context } = compiler.options;
        // 获取 form 的绝对地址
        const absoluteForm = path.isAbsolute(form) ? form : path.resolve(context, form);

        // 获取 ignore 后的匹配目录文件路径
        const paths = await globby(absoluteForm, { ignore });

        // 2. 读取 paths 文件内容
        const files = paths.map((absolutePath) => {
          // 读取文件
          const data = fs.readFileSync(absolutePath);

          // 获取文件名
          const filename = path.basename(absolutePath);

          // filename 和 to 结合，获取文件path
          const filePath = path.join(to, filename);
          return {
            data,
            filename: filePath,
          };
        });
        // 3. 创建webpack格式的资源，并输入

        // webpack 模块实例，可以通过 compiler 对象访问，
        // 这样确保使用的是模块的正确版本
        // （不要直接 require/import webpack）
        const { webpack } = compiler;

        // RawSource 是其中一种 “源码”("sources") 类型，
        // 用来在 compilation 中表示资源的源码
        const { RawSource } = webpack.sources;

        // 向 compilation 添加新的资源，
        // 这样 webpack 就会自动生成并输出到 output 目录
        files.map((file) => compilation.emitAsset(file.filename, new RawSource(file.data)));

        callback();
      });
    });
  }
}

module.exports = CopyWebpackPlugin;
